package com.fast.fast.common.mybatis.plus.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fast.fast.common.base.entity.BaseEntity;
import com.fast.fast.common.base.page.PageResult;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * MybatisPlus自定义Service(泛型：T是数据库实体对象，V是出参视图对象,当不需要VO对象时,V可设为通配符)
 * <p>
 * 用法:
 * 第一步：在相应的service里,先注入:
 * private SuperService<T, ?> service;
 * 第二步：在调用本类方法之前,需要先赋值具体业务实现类
 * superService.setClazz(XXXServiceImpl.class);
 * 增加了:
 * 1.支持自动转换数据库对象T到出参视图对象V,无需再手动转换数据库实体到出参视图对象
 * 2.部分自定义方法(自定义分页)
 * <p>
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 **/
@Service
public class SuperService<T extends BaseEntity, V> {

    /**
     * 传入的IService的实现类
     */

    @Setter
    private Class<?> clazz;

    /**
     * IService的实现类集合
     */
    @Lazy
    @Autowired
    private List<IService<T>> iServices;

    public IService<T> getServiceImpl() {
        IService<T> iService = iServices.stream()
                .filter(o -> o.getClass().getSimpleName().contains(clazz.getSimpleName()))
                .findFirst()
                .orElseThrow(() -> new NullPointerException("未在SuperService中找到IService的实现类"));
        return iService;
    }

    /**
     * 根据id，单个查询
     *
     * @param id    数据库实体对象id
     * @param clazz VO对象的Class
     * @return 单个VO对象
     */
    @Transactional(readOnly = true)
    public V getById(Serializable id, Class<V> clazz) {
        T t = getServiceImpl().getById(id);
        return t == null ? null : BeanUtil.copyProperties(t, clazz);
    }

    /**
     * 根据id，批量查询
     *
     * @param ids   数据库实体对象id集合
     * @param clazz VO对象的Class
     * @return VO对象集合
     */
    @Transactional(readOnly = true)
    public List<V> listByIds(Collection<? extends Serializable> ids, Class<V> clazz) {
        List<T> list = getServiceImpl().listByIds(ids);
        return CollUtil.isEmpty(list) ? null : BeanUtil.copyToList(list, clazz);
    }

    /**
     * 根据wrapper条件，单个查询
     *
     * @param wrapper 条件构造器
     * @param clazz   VO对象的Class
     * @return 单个VO对象
     */
    @Transactional(readOnly = true)
    public V getOne(Wrapper<T> wrapper, Class<V> clazz) {
        T t = getServiceImpl().getOne(wrapper);
        return t == null ? null : BeanUtil.copyProperties(t, clazz);
    }

    /**
     * 根据wrapper条件，批量查询
     *
     * @param wrapper 条件构造器
     * @param clazz   VO对象的Class
     * @return VO对象集合
     */
    @Transactional(readOnly = true)
    public List<V> list(Wrapper<T> wrapper, Class<V> clazz) {
        List<T> list = getServiceImpl().list(wrapper);
        return CollUtil.isEmpty(list) ? null : BeanUtil.copyToList(list, clazz);
    }

    /**
     * 无条件批量查询
     *
     * @param clazz VO对象的Class
     * @return VO对象集合
     */
    @Transactional(readOnly = true)
    public List<V> list(Class<V> clazz) {
        return this.list(Wrappers.emptyWrapper(), clazz);
    }

    /**
     * 无条件分页查询
     *
     * @param t     数据库实体对象
     * @param clazz VO对象的Class
     * @return 自定义分页对象
     */
    @Transactional(readOnly = true)
    public PageResult<V> page(T t, Class<V> clazz) {
        return this.page(t, clazz, Wrappers.emptyWrapper());
    }

    /**
     * 根据wrapper条件，分页查询
     *
     * @param t       数据库实体对象
     * @param clazz   VO对象的Class
     * @param wrapper 条件构造器
     * @return 自定义分页对象
     */
    @Transactional(readOnly = true)
    public PageResult<V> page(T t, Class<V> clazz, Wrapper<T> wrapper) {
        IPage<T> page = getServiceImpl().page(new Page<>(t.getCurrent(), t.getSize()), wrapper);
        List<V> list = BeanUtil.copyToList(page.getRecords(), clazz);
        // 写法二:
        // List<V> list = page.getRecords().stream().map(o -> JSONUtil.toBean(JSONUtil.toJsonStr(o), clazz)).collect(Collectors.toList());
        PageResult<V> pageResult = PageResult.of(page);
        // 对象转换后需要重新赋值
        pageResult.setRecords(list);
        return pageResult;
    }

    /********************************************以下方法不进行对象转换***********************************************/

    /**
     * 无条件分页查询
     *
     * @param t 数据库实体对象
     * @return 自定义分页对象
     */
    @Transactional(readOnly = true)
    public PageResult<T> page(T t) {
        return this.page(t, Wrappers.emptyWrapper());
    }

    /**
     * 根据wrapper条件，分页查询
     *
     * @param t       数据库实体对象
     * @param wrapper 条件构造器
     * @return 自定义分页对象
     */
    @Transactional(readOnly = true)
    public PageResult<T> page(T t, Wrapper<T> wrapper) {
        IPage<T> page = getServiceImpl().page(new Page<>(t.getCurrent(), t.getSize()), wrapper);
        return PageResult.of(page);
    }
}
